Render this report with
~/spinal_cord_paper/scripts/Gg_devel_scWGCNA_module_analysis_render.sh.
library(Seurat)
library(WGCNA)
library(tidyr)
library(ggplot2)
library(stringr)
library(patchwork)
library(tidyverse)
library(cowplot)
library(pheatmap)
library(gridExtra)
source("~/Neuraltube/scripts/heatmap4.R")
Load individual seurat and test WGCNA data
The individual data sets are the Day 5 (Gg_D05_ctrl_seurat_070323),
Day 7 (Gg_D07_ctrl_seurat_070323), and Day 10 (Gg_ctrl_1_seurat_070323)
chicken spinal cord sets. The test WGCNA data are the modules calculated
on the integrated data set of all three stages.
se_path <- c("Gg_D05_ctrl_seurat_070323",
"Gg_D07_ctrl_seurat_070323",
"Gg_ctrl_1_seurat_070323")
clust_col <- read.csv("~/spinal_cord_paper/annotations/broad_cluster_marker_colors.csv") %>%
rename(broad = broad_cluster) %>%
select(-marker)
Order of the broad clusters for plotting purposes.
broad_order <- c("progenitors",
"FP",
"RP",
"FP/RP",
"neurons",
"OPC",
"MFOL",
"pericytes",
"microglia",
"blood",
"vasculature"
)
my.ses <- list()
col_table <- list()
ord_levels <- list()
for (i in seq(se_path)) {
# load the data sets
my.se <- readRDS(paste0("~/spinal_cord_paper/data/", se_path[i], ".rds"))
annot <- read.csv(list.files("~/spinal_cord_paper/annotations",
pattern = str_remove(se_path[i], "_seurat_\\d{6}"),
full.names = TRUE))
if(length(table(annot$number)) != length(table(my.se$seurat_clusters))) {
stop("Number of clusters must be identical!")
}
# rename for left join
annot <- annot %>%
mutate(fine = paste(fine, number, sep = "_")) %>%
mutate(number = factor(number, levels = 1:nrow(annot))) %>%
rename(seurat_clusters = number)
# cluster order for vln plots
ord_levels[[i]] <- annot$fine[order(match(annot$broad, broad_order))]
# create index for color coding
col_table[[i]] <- annot %>%
left_join(clust_col, by = "broad") %>%
select(c("fine", "color"))
# add cluster annotation to meta data
my.se@meta.data <- my.se@meta.data %>%
rownames_to_column("rowname") %>%
left_join(annot, by = "seurat_clusters") %>%
mutate(fine = factor(fine, levels = annot$fine)) %>%
column_to_rownames("rowname")
my.ses[[i]] <- my.se
}
names(my.ses) <- c("D05", "D07", "D10")
names(col_table) <- c("D05", "D07", "D10")
names(ord_levels) <- c("D05", "D07", "D10")
rm(my.se, annot)
# The reference WGCNA data. We can have several, if we want to test many at the same time
WGCNA_data = list()
WGCNA_data[[1]] = readRDS("~/spinal_cord_paper/output/Gg_devel_int_scWGCNA_250723.rds")
my.wsub =list()
my.wsub[[1]]= c(1:22)
# the name of each sample, as they appear in my.files and in the metadata of the combined object
my.samplenames = c("D05", "D07", "D10")
# This is just to add a little bit more sense to the modules, so that we don't get just a color. Corresponds to WGCNA_data
my.modulenames = list()
my.modulenames[[1]] = c(1:22)
Module gene correlation
Here, we do a correlation matrix / heatmap, to see which cell
clusters group togheter. This helps us to make more detailed
dotplots.
This part of the script can still be used to compare several WGCNA
datasets in parallel.
# broad cluster color table
all_col <- do.call(rbind, col_table) %>%
rownames_to_column("sample") %>%
mutate(sample = substr(sample, 1, 3)) %>%
mutate(sample_celltype = paste(sample, fine, sep = "_")) %>%
select(c("color", "sample_celltype", "sample"))
Average expression data
cell_table <- my.metam %>%
rownames_to_column("cell_ID") %>%
select("sample_celltype", "cell_ID")
avg.mod.eigengenes <- WGCNA_data[[1]]$sc.MEList$averageExpr %>%
rownames_to_column("cell_ID") %>%
left_join(cell_table, by = "cell_ID") %>%
column_to_rownames("cell_ID")
# add metadata
avg.mod.eigengenes.mean <- avg.mod.eigengenes %>%
group_by(sample_celltype) %>%
summarise_all("mean") %>%
column_to_rownames("sample_celltype")
spearman correlation heatmap
annotations
# names and colors for the heatmap annotation
annot_name <- data.frame(
"Celltypes" = all_col$sample_celltype,
"Sample" = all_col$sample,
row.names = all_col$sample_celltype)
annot_module <- data.frame(
"Module" = colnames(avg.mod.eigengenes)[1:22],
row.names = colnames(avg.mod.eigengenes)[1:22]
)
pheat_col_table <- do.call(rbind, col_table) %>%
rownames_to_column("sample") %>%
mutate(sample = substr(sample, 1,3)) %>%
mutate(fine = paste(sample, fine, sep = "_"))
# match color table with annotation
pheat_col_table <- pheat_col_table[match(annot_name$Celltypes, pheat_col_table$fine),]
annot_col <- list(
Celltypes = pheat_col_table$color,
Sample = c(D05 = "#A4A4A4",
D07 = "#515151",
D10 = "#000000"),
Module = str_remove(colnames(avg.mod.eigengenes)[1:22], "AE")
)
names(annot_col[[1]]) <- annot_name$Celltypes
names(annot_col[[3]]) <- colnames(avg.mod.eigengenes)[1:22]
heatmap of module pseudobulk average expression
pdf("~/spinal_cord_paper/figures/Fig_1_devel_module_v_clusters_heatmap.pdf", width = 8, height = 10)
grid.arrange(htmp$gtable)
dev.off()
null device
1
Load the integrated data set
The integrated data set on which the WGCNA is calculated. We plot it,
split by the original cell types from the three original samples.
# This is a file of all the combined mouse datasets, normalized and such.
my.sec = readRDS("~/spinal_cord_paper/data/Gg_devel_int_seurat_250723.rds")
identical(rownames(my.metam), colnames(my.sec))
[1] TRUE
my.metam$sample_celltype <- factor(my.metam$sample_celltype, levels = all_col$sample_celltype)
#Set the identities of the integrated data, to the annotated clusters
my.sec = SetIdent(my.sec, value = my.metam$sample_celltype)
p1 <- DimPlot(
my.sec,
reduction = "tsne",
label = TRUE,
repel = TRUE,
cols = all_col$color,
split.by = "orig.ident"
) +
NoLegend()
p1
pdf("~/spinal_cord_paper/figures/Devel_split_tsne.pdf", height = 7, width = 15)
#Plot split tsne
p1

Avg. module exp. by stage
tSNE DimPlots showing the average expression of each module by
stage.

AE over time
We plot the average expression of each module in the three stages and
the 5 broad cell type clusters present in all 3 stages.
VlnPlots of avg. module exp. by stage and seurat cluster
colored by module
# reorder seurat clusters
for (i in seq(my.ses)) {
my.ses[[i]]$seurat_clusters <- factor(
my.ses[[i]]$seurat_clusters,
levels = levels(my.ses[[i]]$seurat_clusters)[as.integer(str_extract(ord_levels[[i]], "\\d{1,2}$"))]
)
}
vplots <- list()
for (i in seq(my.ses)) {
mods <- colnames(my.ses[[i]][[]])[grep("^AE",colnames(my.ses[[i]][[]]))]
vplots[[i]] <- VlnPlot(
my.ses[[i]],
features = mods[module_order],
group.by = "seurat_clusters",
stack = TRUE, flip = TRUE,
cols = substring(mods, 3)[module_order]) +
theme(legend.position = "none") +
geom_hline(yintercept = 0, lty = "dashed")
}
vplots[[1]]
vplots[[2]]
vplots[[3]]
pdf("~/spinal_cord_paper/figures/Fig_2_AE_by_cluster_modcol.pdf", height = 20, width = 10)
vplots[[1]]
vplots[[2]]
vplots[[3]]
colored by cell type
clust_col <- read.csv("~/spinal_cord_paper/annotations/broad_cluster_marker_colors.csv")
vplots_id <- list()
for (i in seq(my.ses)) {
mods <- colnames(my.ses[[i]][[]])[grep("^AE",colnames(my.ses[[i]][[]]))]
vplots_id[[i]] <- VlnPlot(
my.ses[[i]],
features = mods[module_order],
group.by = "seurat_clusters",
stack = TRUE, flip = TRUE,
fill.by = "ident",
cols = col_table[[i]]$color[as.integer(str_extract(ord_levels[[i]], "\\d{1,2}$"))]) +
theme(legend.position = "none") +
geom_hline(yintercept = 0, lty = "dashed")
}
vplots_id[[1]]
vplots_id[[2]]
vplots_id[[3]]
pdf("~/spinal_cord_paper/figures/Fig_2_AE_by_cluster_clucol.pdf", height = 20, width = 10)
vplots_id[[1]]
vplots_id[[2]]
vplots_id[[3]]
MN modules
For the figures we specifically select the two MN modules and plot
them as Vln plots.

VlnPlot(
my.sec,
features = mods[module_order],
group.by = "sample_celltype",
stack = TRUE, flip = TRUE,
cols = substring(mods, 3)[module_order]) +
theme(legend.position = "none") +
geom_hline(yintercept = 0, lty = "dashed")
pdf("~/spinal_cord_paper/figures/Fig_2_AE_by_cluster_integrated_data.pdf", height = 20, width = 30)
VlnPlot(
my.sec,
features = mods[module_order],
group.by = "sample_celltype",
stack = TRUE, flip = TRUE,
cols = substring(mods, 3)[module_order]) +
theme(legend.position = "none") +
geom_hline(yintercept = 0, lty = "dashed")
```r
# Date and time of Rendering
Sys.time()
sessionInfo()
```
LS0tCnRpdGxlOiAiRGV2ZWxfaW50IFdHQ05BIG1vZHVsZXMgZXhwcmVzc2lvbiBpbiBOVCBENSwgRDcsIGFuZCBEMTAgc2FtcGxlcyIKYXV0aG9yOiAiRmFiaW8gU2FjaGVyIgpkYXRlOiAiMDQuMDguMjAyMyIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IFRSVUUKICAgIHRvY19mbG9hdDogVFJVRQogIGh0bWxfbm90ZWJvb2s6CiAgICBmaWdfaGVpZ2h0OiA3CiAgICBmaWdfd2lkdGg6IDgKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KClJlbmRlciB0aGlzIHJlcG9ydCB3aXRoIH4vc3BpbmFsX2NvcmRfcGFwZXIvc2NyaXB0cy9HZ19kZXZlbF9zY1dHQ05BX21vZHVsZV9hbmFseXNpc19yZW5kZXIuc2guCgpgYGB7ciBzZXR1cH0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoV0dDTkEpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShwaGVhdG1hcCkKbGlicmFyeShncmlkRXh0cmEpCmBgYAoKIyBMb2FkIGluZGl2aWR1YWwgc2V1cmF0IGFuZCB0ZXN0IFdHQ05BIGRhdGEKClRoZSBpbmRpdmlkdWFsIGRhdGEgc2V0cyBhcmUgdGhlIERheSA1IChHZ19EMDVfY3RybF9zZXVyYXRfMDcwMzIzKSwgRGF5IDcgKEdnX0QwN19jdHJsX3NldXJhdF8wNzAzMjMpLCBhbmQgRGF5IDEwIChHZ19jdHJsXzFfc2V1cmF0XzA3MDMyMykgY2hpY2tlbiBzcGluYWwgY29yZCBzZXRzLiBUaGUgdGVzdCBXR0NOQSBkYXRhIGFyZSB0aGUgbW9kdWxlcyBjYWxjdWxhdGVkIG9uIHRoZSBpbnRlZ3JhdGVkIGRhdGEgc2V0IG9mIGFsbCB0aHJlZSBzdGFnZXMuCgpgYGB7ciBkYXRhLXNldHN9CnNlX3BhdGggPC0gYygiR2dfRDA1X2N0cmxfc2V1cmF0XzA3MDMyMyIsCiAgICAgICAgICAgICAiR2dfRDA3X2N0cmxfc2V1cmF0XzA3MDMyMyIsCiAgICAgICAgICAgICAiR2dfY3RybF8xX3NldXJhdF8wNzAzMjMiKQoKY2x1c3RfY29sIDwtIHJlYWQuY3N2KCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2Fubm90YXRpb25zL2Jyb2FkX2NsdXN0ZXJfbWFya2VyX2NvbG9ycy5jc3YiKSAlPiUgCiAgcmVuYW1lKGJyb2FkID0gYnJvYWRfY2x1c3RlcikgJT4lIAogIHNlbGVjdCgtbWFya2VyKQoKYGBgCgpPcmRlciBvZiB0aGUgYnJvYWQgY2x1c3RlcnMgZm9yIHBsb3R0aW5nIHB1cnBvc2VzLgoKYGBge3Igb3JkZXJpbmd9CmJyb2FkX29yZGVyIDwtIGMoInByb2dlbml0b3JzIiwKICAgICAgIkZQIiwKICAgICAgIlJQIiwKICAgICAgIkZQL1JQIiwKICAgICAgIm5ldXJvbnMiLAogICAgICAiT1BDIiwKICAgICAgIk1GT0wiLAogICAgICAicGVyaWN5dGVzIiwKICAgICAgIm1pY3JvZ2xpYSIsCiAgICAgICJibG9vZCIsCiAgICAgICJ2YXNjdWxhdHVyZSIKICAgICAgKQpgYGAKCgpgYGB7ciBzZXVyYXQtb2JqZWN0cy1hbmQtYW5ub3RhdGlvbnN9Cm15LnNlcyA8LSBsaXN0KCkKY29sX3RhYmxlIDwtIGxpc3QoKQpvcmRfbGV2ZWxzIDwtIGxpc3QoKQoKZm9yIChpIGluIHNlcShzZV9wYXRoKSkgewogICMgbG9hZCB0aGUgZGF0YSBzZXRzCiAgbXkuc2UgPC0gcmVhZFJEUyhwYXN0ZTAoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZGF0YS8iLCBzZV9wYXRoW2ldLCAiLnJkcyIpKQogIGFubm90IDwtIHJlYWQuY3N2KGxpc3QuZmlsZXMoIn4vc3BpbmFsX2NvcmRfcGFwZXIvYW5ub3RhdGlvbnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9IHN0cl9yZW1vdmUoc2VfcGF0aFtpXSwgIl9zZXVyYXRfXFxkezZ9IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVFJVRSkpCiAgCiAgaWYobGVuZ3RoKHRhYmxlKGFubm90JG51bWJlcikpICE9IGxlbmd0aCh0YWJsZShteS5zZSRzZXVyYXRfY2x1c3RlcnMpKSkgewogICAgIHN0b3AoIk51bWJlciBvZiBjbHVzdGVycyBtdXN0IGJlIGlkZW50aWNhbCEiKQogIH0KICAKICAjIHJlbmFtZSBmb3IgbGVmdCBqb2luCiAgYW5ub3QgPC0gYW5ub3QgJT4lIAogICAgbXV0YXRlKGZpbmUgPSBwYXN0ZShmaW5lLCBudW1iZXIsIHNlcCA9ICJfIikpICU+JSAKICAgIG11dGF0ZShudW1iZXIgPSBmYWN0b3IobnVtYmVyLCBsZXZlbHMgPSAxOm5yb3coYW5ub3QpKSkgJT4lIAogICAgcmVuYW1lKHNldXJhdF9jbHVzdGVycyA9IG51bWJlcikgCiAgCiAgIyBjbHVzdGVyIG9yZGVyIGZvciB2bG4gcGxvdHMKICBvcmRfbGV2ZWxzW1tpXV0gPC0gYW5ub3QkZmluZVtvcmRlcihtYXRjaChhbm5vdCRicm9hZCwgYnJvYWRfb3JkZXIpKV0KICAKICAjIGNyZWF0ZSBpbmRleCBmb3IgY29sb3IgY29kaW5nCiAgY29sX3RhYmxlW1tpXV0gPC0gYW5ub3QgJT4lCiAgICBsZWZ0X2pvaW4oY2x1c3RfY29sLCBieSA9ICJicm9hZCIpICU+JSAKICAgIHNlbGVjdChjKCJmaW5lIiwgImNvbG9yIikpCiAgCiAgIyBhZGQgY2x1c3RlciBhbm5vdGF0aW9uIHRvIG1ldGEgZGF0YQogIG15LnNlQG1ldGEuZGF0YSA8LSBteS5zZUBtZXRhLmRhdGEgJT4lIAogICAgcm93bmFtZXNfdG9fY29sdW1uKCJyb3duYW1lIikgJT4lIAogICAgbGVmdF9qb2luKGFubm90LCBieSA9ICJzZXVyYXRfY2x1c3RlcnMiKSAlPiUgCiAgICBtdXRhdGUoZmluZSA9IGZhY3RvcihmaW5lLCBsZXZlbHMgPSBhbm5vdCRmaW5lKSkgJT4lIAogICAgY29sdW1uX3RvX3Jvd25hbWVzKCJyb3duYW1lIikKICAKICBteS5zZXNbW2ldXSA8LSBteS5zZQoKfQoKbmFtZXMobXkuc2VzKSA8LSBjKCJEMDUiLCAiRDA3IiwgIkQxMCIpCm5hbWVzKGNvbF90YWJsZSkgPC0gYygiRDA1IiwgIkQwNyIsICJEMTAiKQpuYW1lcyhvcmRfbGV2ZWxzKSA8LSBjKCJEMDUiLCAiRDA3IiwgIkQxMCIpCgpybShteS5zZSwgYW5ub3QpCmBgYAoKYGBge3IgaW5wdXQgZGF0YSwgZWNobz1UUlVFfQojIFRoZSByZWZlcmVuY2UgV0dDTkEgZGF0YS4gV2UgY2FuIGhhdmUgc2V2ZXJhbCwgaWYgd2Ugd2FudCB0byB0ZXN0IG1hbnkgYXQgdGhlIHNhbWUgdGltZQpXR0NOQV9kYXRhID0gbGlzdCgpCldHQ05BX2RhdGFbWzFdXSA9IHJlYWRSRFMoIn4vc3BpbmFsX2NvcmRfcGFwZXIvb3V0cHV0L0dnX2RldmVsX2ludF9zY1dHQ05BXzI1MDcyMy5yZHMiKQpteS53c3ViID1saXN0KCkKbXkud3N1YltbMV1dPSBjKDE6MjIpCgojIHRoZSBuYW1lIG9mIGVhY2ggc2FtcGxlLCBhcyB0aGV5IGFwcGVhciBpbiBteS5maWxlcyBhbmQgaW4gdGhlIG1ldGFkYXRhIG9mIHRoZSBjb21iaW5lZCBvYmplY3QKbXkuc2FtcGxlbmFtZXMgPSBjKCJEMDUiLCAiRDA3IiwgIkQxMCIpCgojIFRoaXMgaXMganVzdCB0byBhZGQgYSBsaXR0bGUgYml0IG1vcmUgc2Vuc2UgdG8gdGhlIG1vZHVsZXMsIHNvIHRoYXQgd2UgZG9uJ3QgZ2V0IGp1c3QgYSBjb2xvci4gQ29ycmVzcG9uZHMgdG8gV0dDTkFfZGF0YQpteS5tb2R1bGVuYW1lcyA9IGxpc3QoKQpteS5tb2R1bGVuYW1lc1tbMV1dID0gYygxOjIyKQoKYGBgCgojIE1vZHVsZSBnZW5lIGNvcnJlbGF0aW9uCgpIZXJlLCB3ZSBkbyBhIGNvcnJlbGF0aW9uIG1hdHJpeCAvIGhlYXRtYXAsIHRvIHNlZSB3aGljaCBjZWxsIGNsdXN0ZXJzIGdyb3VwIHRvZ2hldGVyLiBUaGlzIGhlbHBzIHVzIHRvIG1ha2UgbW9yZSBkZXRhaWxlZCBkb3RwbG90cy4gIApUaGlzIHBhcnQgb2YgdGhlIHNjcmlwdCBjYW4gc3RpbGwgYmUgdXNlZCB0byBjb21wYXJlIHNldmVyYWwgV0dDTkEgZGF0YXNldHMgaW4gcGFyYWxsZWwuICAKCmBgYHtyfQojIGJyb2FkIGNsdXN0ZXIgY29sb3IgdGFibGUKYWxsX2NvbCA8LSBkby5jYWxsKHJiaW5kLCBjb2xfdGFibGUpICU+JSAKICByb3duYW1lc190b19jb2x1bW4oInNhbXBsZSIpICU+JSAKICBtdXRhdGUoc2FtcGxlID0gc3Vic3RyKHNhbXBsZSwgMSwgMykpICU+JSAKICBtdXRhdGUoc2FtcGxlX2NlbGx0eXBlID0gcGFzdGUoc2FtcGxlLCBmaW5lLCBzZXAgPSAiXyIpKSAlPiUgCiAgc2VsZWN0KGMoImNvbG9yIiwgInNhbXBsZV9jZWxsdHlwZSIsICJzYW1wbGUiKSkKYGBgCgojIE1ldGEgZGF0YQoKYGBge3IgbWV0YS1kYXRhfQojR2V0IGEgZGF0YWZyYW1lIHdpdGggYW5ub3RhdGlvbnMgZm9yIGFsbCB0aGUgc2FtcGxlcyBhbmQgY29sb3JzIHdlIG5lZWQuCm15Lm1ldGFtIDwtIGxpc3QoKQoKZm9yIChpIGluIHNlcShteS5zZXMpKSB7CiAgbXkubWV0YW1bW2ldXSA8LSBteS5zZXNbW2ldXVtbXV0KICByb3duYW1lcyhteS5tZXRhbVtbaV1dKSA8LSBwYXN0ZTAocm93bmFtZXMobXkubWV0YW1bW2ldXSksICJfIiwgaSkKfQpteS5tZXRhbSA8LSBkby5jYWxsKHJiaW5kLCBteS5tZXRhbSkKCm15Lm1ldGFtJG9yaWcuaWRlbnQgPC0gc3RyX3JlcGxhY2VfYWxsKG15Lm1ldGFtJG9yaWcuaWRlbnQsIHBhdHRlcm4gPSAgIkdnX0QwNV9jdHJsIiwgIkQwNSIpCm15Lm1ldGFtJG9yaWcuaWRlbnQgPC0gc3RyX3JlcGxhY2VfYWxsKG15Lm1ldGFtJG9yaWcuaWRlbnQsIHBhdHRlcm4gPSAgIkdnX0QwN19jdHJsIiwgIkQwNyIpCm15Lm1ldGFtJG9yaWcuaWRlbnQgPC0gc3RyX3JlcGxhY2VfYWxsKG15Lm1ldGFtJG9yaWcuaWRlbnQsIHBhdHRlcm4gPSAgIkdnX2N0cmxfMSIsICJEMTAiKQoKIyBteS5tZXRhbSRzYW1wbGVfY2VsbHR5cGUgPSBwYXN0ZTAoc3Vic3RyKG15Lm1ldGFtJG9yaWcuaWRlbnQsNyw5KSwiXyIsbXkubWV0YW0kc2V1cmF0X2NsdXN0ZXJzKQpteS5tZXRhbSRzYW1wbGVfY2VsbHR5cGUgPSBwYXN0ZTAobXkubWV0YW0kb3JpZy5pZGVudCwgIl8iLCBteS5tZXRhbSRmaW5lKQoKbXkubWV0YW0gPC0gbXkubWV0YW0gJT4lIAogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJjZWxsX0lEIikgJT4lCiAgZHBseXI6OmxlZnRfam9pbihhbGxfY29sLCBieSA9ICJzYW1wbGVfY2VsbHR5cGUiKSAlPiUKICB0aWJibGU6OmNvbHVtbl90b19yb3duYW1lcyh2YXIgPSAiY2VsbF9JRCIpCgoKIyBnZXQgc2FtcGxlIGNvbG9ycwpteS5jb2xzbSA9IGMoImdyZXkiLCAiZ3JleTMwIiwgImJsYWNrIikKbmFtZXMobXkuY29sc20pIDwtIGMoIkQwNSIsICJEMDciLCAiRDEwIikKIApgYGAKCgojIEF2ZXJhZ2UgZXhwcmVzc2lvbiBkYXRhCgpgYGB7cn0KCmNlbGxfdGFibGUgPC0gbXkubWV0YW0gJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbigiY2VsbF9JRCIpICU+JSAKICBzZWxlY3QoInNhbXBsZV9jZWxsdHlwZSIsICJjZWxsX0lEIikKCmF2Zy5tb2QuZWlnZW5nZW5lcyA8LSBXR0NOQV9kYXRhW1sxXV0kc2MuTUVMaXN0JGF2ZXJhZ2VFeHByICU+JSAKICByb3duYW1lc190b19jb2x1bW4oImNlbGxfSUQiKSAlPiUgCiAgbGVmdF9qb2luKGNlbGxfdGFibGUsIGJ5ID0gImNlbGxfSUQiKSAlPiUgCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJjZWxsX0lEIikKICAKCiMgYWRkIG1ldGFkYXRhCgphdmcubW9kLmVpZ2VuZ2VuZXMubWVhbiA8LSBhdmcubW9kLmVpZ2VuZ2VuZXMgJT4lCiAgZ3JvdXBfYnkoc2FtcGxlX2NlbGx0eXBlKSAlPiUKICBzdW1tYXJpc2VfYWxsKCJtZWFuIikgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJzYW1wbGVfY2VsbHR5cGUiKQoKYGBgCgojIyBzcGVhcm1hbiBjb3JyZWxhdGlvbiBoZWF0bWFwCgojIyMgYW5ub3RhdGlvbnMKCmBgYHtyIGFubm90LWxpc3R9CgojIG5hbWVzIGFuZCBjb2xvcnMgZm9yIHRoZSBoZWF0bWFwIGFubm90YXRpb24KYW5ub3RfbmFtZSA8LSBkYXRhLmZyYW1lKAogICJDZWxsdHlwZXMiID0gYWxsX2NvbCRzYW1wbGVfY2VsbHR5cGUsCiAgIlNhbXBsZSIgICAgPSBhbGxfY29sJHNhbXBsZSwKICByb3cubmFtZXMgPSBhbGxfY29sJHNhbXBsZV9jZWxsdHlwZSkKICAKCmFubm90X21vZHVsZSA8LSBkYXRhLmZyYW1lKAogICJNb2R1bGUiID0gY29sbmFtZXMoYXZnLm1vZC5laWdlbmdlbmVzKVsxOjIyXSwKICByb3cubmFtZXMgPSBjb2xuYW1lcyhhdmcubW9kLmVpZ2VuZ2VuZXMpWzE6MjJdCikKCnBoZWF0X2NvbF90YWJsZSA8LSBkby5jYWxsKHJiaW5kLCBjb2xfdGFibGUpICU+JSAKICByb3duYW1lc190b19jb2x1bW4oInNhbXBsZSIpICU+JSAKICBtdXRhdGUoc2FtcGxlID0gc3Vic3RyKHNhbXBsZSwgMSwzKSkgJT4lIAogIG11dGF0ZShmaW5lID0gcGFzdGUoc2FtcGxlLCBmaW5lLCBzZXAgPSAiXyIpKQoKIyBtYXRjaCBjb2xvciB0YWJsZSB3aXRoIGFubm90YXRpb24KcGhlYXRfY29sX3RhYmxlIDwtIHBoZWF0X2NvbF90YWJsZVttYXRjaChhbm5vdF9uYW1lJENlbGx0eXBlcywgcGhlYXRfY29sX3RhYmxlJGZpbmUpLF0KCmFubm90X2NvbCA8LSBsaXN0KAogIENlbGx0eXBlcyA9IHBoZWF0X2NvbF90YWJsZSRjb2xvciwKICBTYW1wbGUgPSBjKEQwNSA9ICIjQTRBNEE0IiwKICAgICAgICAgICAgIEQwNyA9ICIjNTE1MTUxIiwKICAgICAgICAgICAgIEQxMCA9ICIjMDAwMDAwIiksCiAgTW9kdWxlID0gc3RyX3JlbW92ZShjb2xuYW1lcyhhdmcubW9kLmVpZ2VuZ2VuZXMpWzE6MjJdLCAiQUUiKQogICkKCm5hbWVzKGFubm90X2NvbFtbMV1dKSA8LSBhbm5vdF9uYW1lJENlbGx0eXBlcwpuYW1lcyhhbm5vdF9jb2xbWzNdXSkgPC0gY29sbmFtZXMoYXZnLm1vZC5laWdlbmdlbmVzKVsxOjIyXQoKYGBgCgoKIyMgaGVhdG1hcCBvZiBtb2R1bGUgcHNldWRvYnVsayBhdmVyYWdlIGV4cHJlc3Npb24KCmBgYHtyLCAgZmlnLmhlaWdodD0xNSwgZmlnLndpZHRoPTEwfQojIG1vZHVsZSBjb2xvcnMKbXkuY29sY29scyA9IGFzLm1hdHJpeChuYW1lcyh0YWJsZShXR0NOQV9kYXRhW1sxXV0kZHluYW1pY0NvbHMpKSkKCmh0bXAgPC0gcGhlYXRtYXAoYXMubWF0cml4KGF2Zy5tb2QuZWlnZW5nZW5lcy5tZWFuKSwKICAgICAgICAgZm9udHNpemUgPSA4LAogICAgICAgICBzY2FsZSA9ICJjb2x1bW4iLAogICAgICAgICBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygiIzRkMmQ4NyIsIiM3YzU1YTMiLCAid2hpdGUiLCAiI2VkOTkyMSIsICIjODk1ZDI1IikpKG4gPSAxMDAwKSwKICAgICAgICAgYW5ub3RhdGlvbl9yb3cgPSBhbm5vdF9uYW1lLAogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IGFubm90X21vZHVsZSwKICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5vdF9jb2wsCiAgICAgICAgIGFubm90YXRpb25fbGVnZW5kID0gRiwKICAgICAgICAgYm9yZGVyX2NvbG9yID0gTkEpCgptb2R1bGVfb3JkZXIgPC0gaHRtcFtbInRyZWVfY29sIl1dJG9yZGVyCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9GaWdfMV9kZXZlbF9tb2R1bGVfdl9jbHVzdGVyc19oZWF0bWFwLnBkZiIsIHdpZHRoID0gOCwgaGVpZ2h0ID0gMTApCmdyaWQuYXJyYW5nZShodG1wJGd0YWJsZSkKZGV2Lm9mZigpCgpgYGAKCgoKIyBMb2FkIHRoZSBpbnRlZ3JhdGVkIGRhdGEgc2V0CgpUaGUgaW50ZWdyYXRlZCBkYXRhIHNldCBvbiB3aGljaCB0aGUgV0dDTkEgaXMgY2FsY3VsYXRlZC4gV2UgcGxvdCBpdCwgc3BsaXQgYnkgdGhlIG9yaWdpbmFsIGNlbGwgdHlwZXMgZnJvbSB0aGUgdGhyZWUgb3JpZ2luYWwgc2FtcGxlcy4gCgpgYGB7ciBkaW1wbG90cywgZmlnLndpZHRoPTEwfQojIFRoaXMgaXMgYSBmaWxlIG9mIGFsbCB0aGUgY29tYmluZWQgbW91c2UgZGF0YXNldHMsIG5vcm1hbGl6ZWQgYW5kIHN1Y2guCm15LnNlYyA9IHJlYWRSRFMoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZGF0YS9HZ19kZXZlbF9pbnRfc2V1cmF0XzI1MDcyMy5yZHMiKQoKaWRlbnRpY2FsKHJvd25hbWVzKG15Lm1ldGFtKSwgY29sbmFtZXMobXkuc2VjKSkKCm15Lm1ldGFtJHNhbXBsZV9jZWxsdHlwZSA8LSBmYWN0b3IobXkubWV0YW0kc2FtcGxlX2NlbGx0eXBlLCBsZXZlbHMgPSBhbGxfY29sJHNhbXBsZV9jZWxsdHlwZSkKI1NldCB0aGUgaWRlbnRpdGllcyBvZiB0aGUgaW50ZWdyYXRlZCBkYXRhLCB0byB0aGUgYW5ub3RhdGVkIGNsdXN0ZXJzCm15LnNlYyA9IFNldElkZW50KG15LnNlYywgdmFsdWUgPSBteS5tZXRhbSRzYW1wbGVfY2VsbHR5cGUpCgpwMSA8LSBEaW1QbG90KAogIG15LnNlYywKICByZWR1Y3Rpb24gPSAidHNuZSIsCiAgbGFiZWwgPSBUUlVFLAogIHJlcGVsID0gVFJVRSwKICBjb2xzID0gYWxsX2NvbCRjb2xvciwKICBzcGxpdC5ieSA9ICJvcmlnLmlkZW50IgogICkgKyAKICBOb0xlZ2VuZCgpCgpwMQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRGV2ZWxfc3BsaXRfdHNuZS5wZGYiLCBoZWlnaHQgPSA3LCB3aWR0aCA9IDE1KQojUGxvdCBzcGxpdCB0c25lCnAxCmBgYAoKIyBBdmcuIG1vZHVsZSBleHAuIGJ5IHN0YWdlCgp0U05FIERpbVBsb3RzIHNob3dpbmcgdGhlIGF2ZXJhZ2UgZXhwcmVzc2lvbiBvZiBlYWNoIG1vZHVsZSBieSBzdGFnZS4KCmBgYHtyIEFFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00MH0KCmZvciAoaSBpbiBzZXEobXkuc2VzKSkgewogICMgcHJlcGFyZSBhdmVyYWdlIGV4cHJlc3Npb24gdGFibGUKICB0bXAgPC0gYXZnLm1vZC5laWdlbmdlbmVzWywxOjIyXSAlPiUKICAgIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJjZWxsX0lEIikgJT4lCiAgICBkcGx5cjo6ZmlsdGVyKGdyZXBsKHBhc3RlMCgiXyIsIGksICIkIiksIGNlbGxfSUQpKSAlPiUKICAgIGRwbHlyOjptdXRhdGUoY2VsbF9JRCA9IHN0cmluZ3I6OnN0cl9yZW1vdmVfYWxsKGNlbGxfSUQsIHBhc3RlMCgiXyIsIGkpKSkgJT4lCiAgICB0aWJibGU6OmNvbHVtbl90b19yb3duYW1lcygiY2VsbF9JRCIpCiAgCiAgaWRlbnRpY2FsKHJvd25hbWVzKHRtcCksIGNvbG5hbWVzKG15LnNlc1tbaV1dKSkKICAjIGFkZCBtZXRhIGRhdGEgdG8gdGhlIHNldXJhdCBvYmplY3RzCiAgbXkuc2VzW1tpXV0gPC0gQWRkTWV0YURhdGEobXkuc2VzW1tpXV0sIHRtcCkKfQoKI21heCBhbmQgbWluIGV4cHJlc3Npb24gcGVyIG1vZHVsZSAoY29sdW1uIG1heCkKbW9kX21heCA8LSBhcHBseShhdmcubW9kLmVpZ2VuZ2VuZXNbLDE6MjJdLCBNQVJHSU4gPSAyLCBGVU4gPSBtYXgpW21vZHVsZV9vcmRlcl0KbW9kX21pbiA8LSBhcHBseShhdmcubW9kLmVpZ2VuZ2VuZXNbLDE6MjJdLCBNQVJHSU4gPSAyLCBGVU4gPSBtaW4pW21vZHVsZV9vcmRlcl0KCm1vZHBsb3RzIDwtIGxpc3QoKQptb2RwbG90c1tbMV1dIDwtIGxpc3QoKQptb2RwbG90c1tbMl1dIDwtIGxpc3QoKQptb2RwbG90c1tbM11dIDwtIGxpc3QoKQoKbW9kdWxlc19pbl9vcmRlciA8LSBjb2xuYW1lcyh0bXApW21vZHVsZV9vcmRlcl0KCiMgcGxvdCB0aGUgbW9kdWxlcyBzcGxpdCB0byB0aGUgc3RhZ2VzCmZvciAoaSBpbiBzZXEobXkuc2VzKSkgewogIGZvciAoaiBpbiBzZXEobmNvbCh0bXApKSkgewogIAogICAgbW9kcGxvdHNbW2ldXVtbal1dICA8LSBGZWF0dXJlUGxvdCgKICAgICAgbXkuc2VzW1tpXV0sIG9yZGVyID0gVFJVRSwKICAgICAgZmVhdHVyZXMgPSBtb2R1bGVzX2luX29yZGVyW2pdLAogICAgICByZWR1Y3Rpb24gPSAidHNuZSIKICAgICAgKSArCiAgICAgIGdndGl0bGUoc3RyaW5ncjo6c3RyX3JlbW92ZShtb2R1bGVzX2luX29yZGVyW2pdLCJeQUUiKSkgKwogICAgICBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3c9Iml2b3J5MiIsIGhpZ2g9c3Vic3RyaW5nKG1vZHVsZXNfaW5fb3JkZXJbal0sIDMpLCAjY29sb3JzIGluIHRoZSBzY2FsZQogICAgICAgICAgICAgICAgIGxpbWl0cz1jKG1vZF9taW5bal0sIG1vZF9tYXhbal0pKSAjc2FtZSBsaW1pdHMgZm9yIHBsb3RzCgogICAgCiAgICB9Cn0KCmZ1bGxfcGxvdCA8LSBjKG1vZHBsb3RzW1sxXV0sIG1vZHBsb3RzW1syXV0sIG1vZHBsb3RzW1szXV0pCgpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShncm9icyA9IGZ1bGxfcGxvdCwgbmNvbCA9IDMsIGFzLnRhYmxlID0gRkFMU0UpCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9GaWdfMl9kZXZlbF9tb2R1bGVzX0FFX3Bsb3RzLnBkZiIsIHdpZHRoID0gMTIsIGhlaWdodCA9IDcwKQpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShncm9icyA9IGZ1bGxfcGxvdCwgbmNvbCA9IDMsIGFzLnRhYmxlID0gRkFMU0UpCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9TdXBwX0ZpZ18yX21vZHVsZXNfZGFya2dyZWVuX0FFX3Bsb3RzLnBkZiIsIHdpZHRoID0gMTMsIGhlaWdodCA9IDQpCihmdWxsX3Bsb3RbWzldXSArIGZ1bGxfcGxvdFtbMzFdXSArIGZ1bGxfcGxvdFtbNTNdXSkgKyBwbG90X2xheW91dChuY29sID0gMywgZ3VpZGVzID0gImNvbGxlY3QiKQpgYGAKCiMgQUUgb3ZlciB0aW1lCgpXZSBwbG90IHRoZSBhdmVyYWdlIGV4cHJlc3Npb24gb2YgZWFjaCBtb2R1bGUgaW4gdGhlIHRocmVlIHN0YWdlcyBhbmQgdGhlIDUgYnJvYWQgY2VsbCB0eXBlIGNsdXN0ZXJzIHByZXNlbnQgaW4gYWxsIDMgc3RhZ2VzLgoKYGBge3IgQUUtb3Zlci10aW1lLCBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD0xMn0KIyBtb2R1bGUgYW5ub3RhdGlvbnMKbW9kX2Fubm90IDwtIHJlYWQuY3N2KCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2Fubm90YXRpb25zL0dnX2RldmVsX2ludF9zY1dHQ05BX21vZHVsZV9hbm5vdGF0aW9uLmNzdiIpICU+JQogIGRwbHlyOjptdXRhdGUobW9kdWxlID0gc3RyX3JlcGxhY2VfYWxsKG1vZHVsZSwgIlxcZHsxLDJ9XFxfIiwgIkFFIikpCgptZXRhIDwtIGxpc3QoKQoKZm9yIChpIGluIHNlcShteS5zZXMpKSB7CiAgbWV0YVtbaV1dIDwtIG15LnNlc1tbaV1dQG1ldGEuZGF0YSAlPiUKICAgIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJjZWxsX0lEIikgJT4lCiAgICBkcGx5cjo6bXV0YXRlKGNlbGxfSUQgPSBwYXN0ZTAoY2VsbF9JRCwgIl8iLCBpKSkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KGMoImNlbGxfSUQiLCAiYnJvYWQiKSkKfQoKbWV0YSA8LSBkby5jYWxsKHJiaW5kLCBtZXRhKQoKIyBtZWFuIGF2ZXJhZ2UgZXhwcmVzc2lvbiBieSBzdGFnZQptZWFuX0FFIDwtIGF2Zy5tb2QuZWlnZW5nZW5lc1ssMToyMl0gJT4lCiAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oImNlbGxfSUQiKSAlPiUKICBkcGx5cjo6bXV0YXRlKHN0YWdlID0gc3RyaW5ncjo6c3RyX3N1YihjZWxsX0lELCAtMSkpICU+JQogIGRwbHlyOjptdXRhdGUoc3RhZ2UgPSBmYWN0b3Ioc3RhZ2UsIGxldmVscyA9IGMoMTozKSwgbGFiZWxzID0gYygiRDA1IiwgIkQwNyIsICJEMTAiKSkpICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4obWV0YSwgYnkgPSAiY2VsbF9JRCIpICU+JQogIHRpYmJsZTo6Y29sdW1uX3RvX3Jvd25hbWVzKCJjZWxsX0lEIikgJT4lCiAgdGlkeXI6OnVuaXRlKCJzdGFnZV9jbCIsIHN0YWdlLCBicm9hZCwgc2VwID0gIl8iKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoc3RhZ2VfY2wpICU+JQogIGRwbHlyOjpzdW1tYXJpc2VfZWFjaChtZWFuKSAlPiUKICBkcGx5cjo6dW5ncm91cCgpICU+JQogIGdhdGhlcihrZXk9Im1vZHVsZSIsIHZhbHVlID0gIkFFIiwgLXN0YWdlX2NsKSAlPiUKICBkcGx5cjo6bGVmdF9qb2luKG1vZF9hbm5vdFssIGMoMSwzKV0sIGJ5ID0gIm1vZHVsZSIpICU+JQogIHRpZHlyOjpzZXBhcmF0ZSgic3RhZ2VfY2wiLCBjKCJzdGFnZSIsICJicm9hZCIpLCBzZXAgPSAiXyIsIHJlbW92ZSA9IEZBTFNFKSAKCmxhYmVsc19kb3RwbG90IDwtIHN0cmluZ3I6OnN0cl9yZW1vdmUobW9kdWxlc19pbl9vcmRlciwgIl5BRSIpCm5hbWVzKGxhYmVsc19kb3RwbG90KSA8LSBtb2R1bGVzX2luX29yZGVyCgpwbG90X2NsdXN0ZXJzIDwtIGMoInByb2dlbml0b3JzIiwgIm5ldXJvbnMiLCAiUlAiLCAiRlAiLCAicGVyaWN5dGVzIiwgIk9QQyIsICJNRk9MIiwgIm1pY3JvZ2xpYSIsICJibG9vZCIpCgptZWFuX21vZCA8LSBnZ3Bsb3QoZGF0YSA9IG1lYW5fQUUsCiAgYWVzKAogICAgeCA9IHN0YWdlLAogICAgeSA9IEFFLAogICAgY29sb3IgPSBmYWN0b3IoYnJvYWQsIGxldmVscyA9IHBsb3RfY2x1c3RlcnMpLAogICAgZ3JvdXAgPSBicm9hZCwKICAgIGxhYmVsID0gYW5ub3RhdGlvbgogICAgKQogICkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjbHVzdF9jb2wkY29sb3JbbWF0Y2gocGxvdF9jbHVzdGVycywgY2x1c3RfY29sJGJyb2FkKV0pICsKICB0aGVtZV9idygpICsKICAjIGZhY2V0IHdyYXAgd2l0aCByZW9yZGVyZWQgZmFjdG9ycwogIGZhY2V0X3dyYXAodmFycyhmYWN0b3IobW9kdWxlLCBsZXZlbHMgPSB1bmlxdWUobWVhbl9BRSRtb2R1bGUpW21vZHVsZV9vcmRlcl0pKSwKICAgICAgICAgICAgIHNjYWxlcyA9ICJmcmVlX3kiLAogICAgICAgICAgICAgbnJvdyA9IDQsCiAgICAgICAgICAgICBuY29sID0gNikgKwogIGxhYnMoY29sb3IgPSAiYnJvYWQiKSArCiAgZ2d0aXRsZSgiQXZlcmFnZSBtb2R1bGUgZXhwcmVzc2lvbiBieSBzdGFnZSIpCgpwbG90bHk6OmdncGxvdGx5KG1lYW5fbW9kKQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzJfbWVhbl9tb2RfQUUucGRmIiwgaGVpZ2h0ID0gNywgd2lkdGggPSAxMikKI1Bsb3Qgc3BsaXQgdHNuZQptZWFuX21vZApgYGAKCiMgVmxuUGxvdHMgb2YgYXZnLiBtb2R1bGUgZXhwLiBieSBzdGFnZSBhbmQgc2V1cmF0IGNsdXN0ZXIgCgojIyBjb2xvcmVkIGJ5IG1vZHVsZQoKYGBge3IgYXZlcmFnZS1tb2R1bGUtZXhwcmVzc2lvbi1wZXItY2x1c3RlciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTIwfQoKIyByZW9yZGVyIHNldXJhdCBjbHVzdGVycwpmb3IgKGkgaW4gc2VxKG15LnNlcykpIHsKICBteS5zZXNbW2ldXSRzZXVyYXRfY2x1c3RlcnMgPC0gZmFjdG9yKAogIG15LnNlc1tbaV1dJHNldXJhdF9jbHVzdGVycywKICBsZXZlbHMgPSBsZXZlbHMobXkuc2VzW1tpXV0kc2V1cmF0X2NsdXN0ZXJzKVthcy5pbnRlZ2VyKHN0cl9leHRyYWN0KG9yZF9sZXZlbHNbW2ldXSwgIlxcZHsxLDJ9JCIpKV0KICApCgp9Cgp2cGxvdHMgPC0gbGlzdCgpCgpmb3IgKGkgaW4gc2VxKG15LnNlcykpIHsKICAKICBtb2RzIDwtIGNvbG5hbWVzKG15LnNlc1tbaV1dW1tdXSlbZ3JlcCgiXkFFIixjb2xuYW1lcyhteS5zZXNbW2ldXVtbXV0pKV0KICAKICB2cGxvdHNbW2ldXSA8LSBWbG5QbG90KAogICAgICAgIG15LnNlc1tbaV1dLAogICAgICAgIGZlYXR1cmVzID0gbW9kc1ttb2R1bGVfb3JkZXJdLAogICAgICAgIGdyb3VwLmJ5ID0gInNldXJhdF9jbHVzdGVycyIsCiAgICAgICAgc3RhY2sgPSBUUlVFLCBmbGlwID0gVFJVRSwKICAgICAgICBjb2xzID0gc3Vic3RyaW5nKG1vZHMsIDMpW21vZHVsZV9vcmRlcl0pICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbHR5ID0gImRhc2hlZCIpCiAgCn0KCnZwbG90c1tbMV1dCnZwbG90c1tbMl1dCnZwbG90c1tbM11dCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9GaWdfMl9BRV9ieV9jbHVzdGVyX21vZGNvbC5wZGYiLCBoZWlnaHQgPSAyMCwgd2lkdGggPSAxMCkKdnBsb3RzW1sxXV0KdnBsb3RzW1syXV0KdnBsb3RzW1szXV0KYGBgCgojIyBjb2xvcmVkIGJ5IGNlbGwgdHlwZQoKYGBge3IgdmxuLWJ5LWNsdXN0ZXIsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0yMH0KY2x1c3RfY29sIDwtIHJlYWQuY3N2KCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2Fubm90YXRpb25zL2Jyb2FkX2NsdXN0ZXJfbWFya2VyX2NvbG9ycy5jc3YiKQoKdnBsb3RzX2lkIDwtIGxpc3QoKQoKZm9yIChpIGluIHNlcShteS5zZXMpKSB7CiAgICAKICBtb2RzIDwtIGNvbG5hbWVzKG15LnNlc1tbaV1dW1tdXSlbZ3JlcCgiXkFFIixjb2xuYW1lcyhteS5zZXNbW2ldXVtbXV0pKV0KICAKICB2cGxvdHNfaWRbW2ldXSA8LSBWbG5QbG90KAogICAgbXkuc2VzW1tpXV0sCiAgICBmZWF0dXJlcyA9IG1vZHNbbW9kdWxlX29yZGVyXSwKICAgIGdyb3VwLmJ5ID0gInNldXJhdF9jbHVzdGVycyIsCiAgICBzdGFjayA9IFRSVUUsIGZsaXAgPSBUUlVFLAogICAgZmlsbC5ieSA9ICJpZGVudCIsCiAgICBjb2xzID0gY29sX3RhYmxlW1tpXV0kY29sb3JbYXMuaW50ZWdlcihzdHJfZXh0cmFjdChvcmRfbGV2ZWxzW1tpXV0sICJcXGR7MSwyfSQiKSldKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGx0eSA9ICJkYXNoZWQiKQp9Cgp2cGxvdHNfaWRbWzFdXQp2cGxvdHNfaWRbWzJdXQp2cGxvdHNfaWRbWzNdXQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzJfQUVfYnlfY2x1c3Rlcl9jbHVjb2wucGRmIiwgaGVpZ2h0ID0gMjAsIHdpZHRoID0gMTApCnZwbG90c19pZFtbMV1dCnZwbG90c19pZFtbMl1dCnZwbG90c19pZFtbM11dCmBgYAoKIyBNTiBtb2R1bGVzCgpGb3IgdGhlIGZpZ3VyZXMgd2Ugc3BlY2lmaWNhbGx5IHNlbGVjdCB0aGUgdHdvIE1OIG1vZHVsZXMgYW5kIHBsb3QgdGhlbSBhcyBWbG4gcGxvdHMuCgpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTV9CnRtcCA8LSBhdmcubW9kLmVpZ2VuZ2VuZXMgCiAgCmlkZW50aWNhbChyb3duYW1lcyh0bXApLCBjb2xuYW1lcyhteS5zZWMpKQojIGFkZCBtZXRhIGRhdGEgdG8gdGhlIGludCBzZXVyYXQgb2JqZWN0Cm15LnNlYyA8LSBBZGRNZXRhRGF0YShteS5zZWMsIHRtcCkKbXkuc2VjIDwtIEFkZE1ldGFEYXRhKG15LnNlYywgbXkubWV0YW1bYygiZmluZSIsICJzYW1wbGVfY2VsbHR5cGUiKV0pCgpjdXN0b21fb3JkZXIgPC0gYyhwYXN0ZShuYW1lcyhvcmRfbGV2ZWxzKVsxXSwgb3JkX2xldmVsc1tbMV1dLCBzZXAgPSAnXycpLAogICAgICAgICAgICAgICAgICBwYXN0ZShuYW1lcyhvcmRfbGV2ZWxzKVsyXSwgb3JkX2xldmVsc1tbMl1dLCBzZXAgPSAnXycpLAogICAgICAgICAgICAgICAgICBwYXN0ZShuYW1lcyhvcmRfbGV2ZWxzKVszXSwgb3JkX2xldmVsc1tbM11dLCBzZXAgPSAnXycpKQoKbXkuc2VjJHNhbXBsZV9jZWxsdHlwZSA8LSBmYWN0b3IoCiAgbXkuc2VjJHNhbXBsZV9jZWxsdHlwZSwKICBsZXZlbHMgPSBjdXN0b21fb3JkZXIKICApCgp2bG5faW5kIDwtIFZsblBsb3QobXkuc2VjLAogICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBjKCJBRWRhcmtyZWQiLCAiQUVsaWdodGdyZWVuIiwgIkFFZGFya2dyZWVuIiksCiAgICAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJzYW1wbGVfY2VsbHR5cGUiLAogICAgICAgICAgICAgICAgICAgc3RhY2sgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgZmxpcCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICBjb2xzID0gYygiZGFya3JlZCIsICJsaWdodGdyZWVuIiwgImRhcmtncmVlbiIpLHB0LnNpemUgPSAxCiAgICAgICAgICAgICAgICAgICkgKwogICAgTm9MZWdlbmQoKQoKdmxuX2luZApwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9GaWdfMl9BRV9zZWxlY3RlZF9tb2QucGRmIiwgaGVpZ2h0ID0gMjIsIHdpZHRoID0gMjApCnZsbl9pbmQKYGBgCgpgYGB7cn0KVmxuUGxvdCgKICAgIG15LnNlYywKICAgIGZlYXR1cmVzID0gbW9kc1ttb2R1bGVfb3JkZXJdLAogICAgZ3JvdXAuYnkgPSAic2FtcGxlX2NlbGx0eXBlIiwKICAgIHN0YWNrID0gVFJVRSwgZmxpcCA9IFRSVUUsCiAgICBjb2xzID0gc3Vic3RyaW5nKG1vZHMsIDMpW21vZHVsZV9vcmRlcl0pICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbHR5ID0gImRhc2hlZCIpCgoKCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ18yX0FFX2J5X2NsdXN0ZXJfaW50ZWdyYXRlZF9kYXRhLnBkZiIsIGhlaWdodCA9IDIwLCB3aWR0aCA9IDMwKQpWbG5QbG90KAogICAgbXkuc2VjLAogICAgZmVhdHVyZXMgPSBtb2RzW21vZHVsZV9vcmRlcl0sCiAgICBncm91cC5ieSA9ICJzYW1wbGVfY2VsbHR5cGUiLAogICAgc3RhY2sgPSBUUlVFLCBmbGlwID0gVFJVRSwKICAgIGNvbHMgPSBzdWJzdHJpbmcobW9kcywgMylbbW9kdWxlX29yZGVyXSkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsdHkgPSAiZGFzaGVkIikKCgpgYGAKCmBgYHtyfQojIERhdGUgYW5kIHRpbWUgb2YgUmVuZGVyaW5nClN5cy50aW1lKCkKCnNlc3Npb25JbmZvKCkKYGBgCgo=